package geppetto.cat.models;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

/**
 * 
 * @author javg
 *
 */
public class DiagonalDistanceDistortionTable {

	float[] _probs;

	int _numberBuckets;

	public DiagonalDistanceDistortionTable(int size) {
		_numberBuckets = 2 * size + 1;
		_probs = new float[_numberBuckets];
	}

	public DiagonalDistanceDistortionTable(String directory) {
		// Initialize hash tables

		System.out.println("------------Loading DiagonalDistortionTable");
		String inFilename = directory + "diagonalDistortion.gz";
		try {
			DataInputStream data_in = new DataInputStream(new GZIPInputStream(
					new FileInputStream(inFilename)));
			int distortionBuckets = data_in.readInt();
			_numberBuckets = distortionBuckets;
			_probs = new float[_numberBuckets];
			while (true) {
				try {
					int bucket = data_in.readInt();
					float prob = data_in.readFloat();
					_probs[bucket] = prob;
				} catch (EOFException eof) {
					System.out.println("FINISHED READING FILE");
					break;

				}
			}
			data_in.close();
		} catch (FileNotFoundException e) {
			System.out.println("Diagonal Distance Table does not exits");
			System.exit(1);
		} catch (IOException e) {
			System.out.println("Error reading Diagonal Distance");
			System.exit(1);
		}
		System.out.println("------------Loading Table END");
	}

	public boolean equals(DiagonalDistanceDistortionTable table) {
		for (int i = 0; i < _numberBuckets; i++) {
			if (_probs[i] != table._probs[i])
				return false;
		}
		return true;
	}

	public void saveTable(String directory) {
		try {
			System.out.println("------------Saving diagonalDistortion Table");
			String outFilename = directory + "diagonalDistortion.gz";
			DataOutputStream out = new DataOutputStream(new GZIPOutputStream(
					new FileOutputStream(outFilename)));
			out.writeInt(_numberBuckets);
			for (int i = 0; i < _numberBuckets; i++) {
				out.writeInt(i);
				out.writeFloat(_probs[i]);
			}
			out.close();
		} catch (IOException e) {
			System.out.println("Error saving diagonalDistortion Table");
			System.exit(1);
		}
		System.out.println("------------Saving DiagonalDistortion Table END");
	}

	// Initializing all distances to the same probability.
	public void initialize() {
		for (int i = 0; i < _numberBuckets; i++) {
			_probs[i] = 1f / _numberBuckets;
		}
	}

	public int getNRBuckets() {
		return _numberBuckets;
	}

	// Receives bucket index
	public float getBucketProbability(int bucket) {
		float sum = 0f;
		for (int i = 0; i < _numberBuckets; i++) {
			sum += _probs[i];
		}
		assert !Double.isNaN(sum);
		// System.out.println("Distorition prob" + sum+"-"+_probs[bucket]/sum);
		return _probs[bucket] / sum;
	}

	public void setBucketProbability(int bucket, float prob) {
		// System.out.println("Adding prob to bucket " + bucket);
		_probs[bucket] = prob;
	}

	// Distences bigger than five are considered as being five
	int calculatePosition(int sourcePos, int targetPos, int sourceLenght,
			int targetLenght) {
		// int diff = Math.round(sourcePos -
		// (1f*targetPos*sourceLenght)/targetLenght);
		int diff = (int) ((sourcePos - (1f * targetPos * sourceLenght)
				/ targetLenght));
		if (diff >= _numberBuckets / 2)
			return _numberBuckets / 2;
		if (diff <= -_numberBuckets / 2)
			return -_numberBuckets / 2;
		return diff;
	}

	public float getProbability(int sourcePos, int targetPos, int sourceLenght,
			int targetLenght) {
		// System.out.println("Distoriotn probability " +
		// _probs[calculatePosition(sourcePos, targetPos, sourceLenght,
		// targetLenght) + 5 ]);
		return _probs[calculatePosition(sourcePos, targetPos, sourceLenght,
				targetLenght)
				+ _numberBuckets / 2];
	}

	public void setProbability(int sourcePos, int targetPos, int sourceLenght,
			int targetLenght, float prob) {
		// System.out.println("Received sourcePos " + sourcePos + "targer pos "+
		// targetPos + "placing at " + (calculatePosition(sourcePos, targetPos,
		// sourceLenght, targetLenght)));
		_probs[calculatePosition(sourcePos, targetPos, sourceLenght,
				targetLenght)
				+ _numberBuckets / 2] = prob;
	}

	public void clear() {
		_probs = new float[_numberBuckets];
	}

	public boolean testProbabilities() {
		float sum = 0;
		for (int i = 0; i < _probs.length; i++) {
			sum += _probs[i];
		}
		if (sum > 1.0002 || sum < 0) { // Given 0.0002 for rounding purposes
			System.out.println("Probs for distortion table not correct " + sum);
			return false;
		}
		System.out.println("Distortion Table correct");
		return true;
	}

	public void printTable() {
		printTable(System.out);
	}

	public void printTable(PrintStream out) {
		for (int i = 0; i < _numberBuckets; i++) {
			out.println("P(" + (i - _numberBuckets / 2) + ")=" + _probs[i]);
		}
	}

}
